
import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Random;
import java.util.Scanner;

/*
 * To change this template, choose Tools | Templates and open the template in
 * the editor.
 */
/**
 *
 * @author Daisuke
 */
public class Sender {

    private Socket socket;
    private Listener oreille;
    private byte[] peer_id;

    public Sender(Socket socket) throws UnsupportedEncodingException {
        this.socket = socket;
        this.oreille = new Listener(socket);
        this.peer_id = generatePeerId();
    }

    public void go() throws IOException {

        this.oreille.start();
        int choix = 1;

        while (choix != 0) {
            afficheMenu();
            Scanner sc = new Scanner(System.in);
            choix = sc.nextInt();

            System.out.println("Vous avez tapez :" + choix);
            
            switch (choix) {
                case 0:
                    this.oreille.interrupt();
                    this.socket.close();
                    break;
                case 1:
                    sendHandshake();
                    break;
                case 2:
                    sendBitfield();
                    break;
                case 3:
                    sendHave();
                    break;
                case 4:
                    sendRequest();
                    break;
                case 5:
                    sendPiece();
                    break;
                case 6:
                    sendCancel();
                    break;
                case 7:
                    sendChoke();
                    break;
                case 8:
                    sendUnchoke();
                    break;
                case 9:
                    sendInterested();
                    break;
                case 10:
                    sendNotInterested();
                    break;
                case 11:
                    sendKeepALive();
                    break;
                default:
                    System.out.println("Erreur de saisie !");
            }

        }

    }

    private void afficheMenu() {

        System.out.println("Que voulez-vous faire ?");
        System.out.println("0) quitter");
        System.out.println("1) envoyer un handshake");
        System.out.println("2) envoyer un (faux) bitfield");
        System.out.println("3) envoyer un Have");
        System.out.println("4) envoyer un Request");
        System.out.println("5) envoyer une (fausse pièce)");
        System.out.println("6) envoyer une annulation de request");
        System.out.println("7) envoyer un Choke");
        System.out.println("8) envoyer un Unchoke");
        System.out.println("9) envoyer un Interested");
        System.out.println("10) envoyer un Not-Interested");
        System.out.println("11) envoyer un Keep-Alive");
    }

    //<len=0013><id=8><index><begin><length>
    private void sendCancel() throws IOException {
        OutputStream out = this.socket.getOutputStream();
        out.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(13).order(ByteOrder.BIG_ENDIAN).array());
        out.write(8);
        out.write(ByteBuffer.allocate(4).putInt(0).order(ByteOrder.LITTLE_ENDIAN).array());
        out.write(ByteBuffer.allocate(4).putInt(0).order(ByteOrder.LITTLE_ENDIAN).array());
        out.write(ByteBuffer.allocate(4).putInt(42).order(ByteOrder.LITTLE_ENDIAN).array());

    }

    //<pstrlen><pstr><reserved><info_hash><peer_id>
    private void sendHandshake() throws IOException {
        OutputStream out = this.socket.getOutputStream();
        out.write(19);
        out.write("BitTorrent protocol".getBytes());
        byte[] reservedbytes = new byte[8];
        out.write(reservedbytes);
        out.write(this.peer_id); // N'ayant pas de hash_info sous la main...
        out.write(this.peer_id);

    }

    //<len=0005><id=4><piece index>
    private void sendHave() throws IOException {
        OutputStream out = this.socket.getOutputStream();
        System.out.println("Quel est l'index de la pièce que vous possédez désormais ?");
        Scanner sc = new Scanner(System.in);
        int index = sc.nextInt();

        ByteBuffer len = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(5).order(ByteOrder.BIG_ENDIAN);
        out.write(len.array());
        out.write(4);
        out.write(ByteBuffer.allocate(4).putInt(index).order(ByteOrder.LITTLE_ENDIAN).array());
    }

    //<len=0001+X><id=5><bitfield>
    private void sendBitfield() throws IOException {
        OutputStream out = this.socket.getOutputStream();

        System.out.println("Combien y-a-t'il de pièces ?");
        Scanner sc = new Scanner(System.in);
        int nb_piece = sc.nextInt();        
        byte[] bitfield = new byte[(nb_piece / 8) + 1];
        System.out.println("On considérera que vous avez toutes les pièces");
        for (int i = 0; i <= nb_piece; i++) {
            setBit(bitfield, i, 1);
        }
        ByteBuffer len = ByteBuffer.allocate(4);
        len.order(ByteOrder.LITTLE_ENDIAN).putInt(1+bitfield.length).order(ByteOrder.BIG_ENDIAN);
        out.write(len.array());
        out.write(5);
        out.write(bitfield);

    }

    //<len=0013><id=6><index><begin><length>
    private void sendRequest() throws IOException {
    
        OutputStream out = this.socket.getOutputStream();
        out.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(13).order(ByteOrder.BIG_ENDIAN).array());
        out.write(6);
        out.write(ByteBuffer.allocate(4).putInt(0).order(ByteOrder.LITTLE_ENDIAN).array());
        out.write(ByteBuffer.allocate(4).putInt(0).order(ByteOrder.LITTLE_ENDIAN).array());
        out.write(ByteBuffer.allocate(4).putInt(42).order(ByteOrder.LITTLE_ENDIAN).array());
        
        
    }

    //<len=0009+X><id=7><index><begin><block>
    private void sendPiece() throws IOException {
        OutputStream out = this.socket.getOutputStream();
        System.out.println("Je n'enverrais pas de pièce tant que je n'aurais pas été reliée à une fonction getPiece");
    }

    private void sendInterested() throws IOException {
        OutputStream out = this.socket.getOutputStream();
        ByteBuffer len = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(1).order(ByteOrder.BIG_ENDIAN);
        out.write(len.array());
        out.write(2);
    }

    private void sendNotInterested() throws IOException {
        OutputStream out = this.socket.getOutputStream();
        ByteBuffer len = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(1).order(ByteOrder.BIG_ENDIAN);
        out.write(len.array());
        out.write(3);
    }

    private void sendUnchoke() throws IOException {
        OutputStream out = this.socket.getOutputStream();
        ByteBuffer len = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(1).order(ByteOrder.BIG_ENDIAN);
        out.write(len.array());
        out.write(1);
    }

    private void sendChoke() throws IOException {
        OutputStream out = this.socket.getOutputStream();
        ByteBuffer len = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(1).order(ByteOrder.BIG_ENDIAN);
        out.write(len.array());
        out.write(0);
    }

    private void sendKeepALive() throws IOException {
        OutputStream out = this.socket.getOutputStream();
        out.write(0);
    }

    private static byte[] generatePeerId() throws UnsupportedEncodingException {

        byte[] peer_id = new byte[20];
        byte[] version = "-DS1337-".getBytes();
        Random random = new Random();

//        for (int i = 0; i < version.length; i++) {
//            peer_id[i] = version[i];
//        }

        System.arraycopy(version, 0, peer_id, 0, version.length);

        String chars = "0123456789";

        for (int i = version.length; i < 20; i++) {
            char carac = chars.charAt(random.nextInt(10));
            peer_id[i] = ((byte) ((int) carac));
        }
        return peer_id;

    }

    private static void setBit(byte[] data, int pos, int val) {
        int posByte = pos / 8;
        int posBit = pos % 8;
        byte oldByte = data[posByte];
        oldByte = (byte) (((0xFF7F >> posBit) & oldByte) & 0x00FF);
        byte newByte = (byte) ((val << (8 - (posBit + 1))) | oldByte);
        data[posByte] = newByte;
    }

    private static int getBit(byte[] data, int pos) {
        int posByte = pos / 8;
        int posBit = pos % 8;
        byte valByte = data[posByte];
        int valInt = valByte >> (8 - (posBit + 1)) & 0x0001;
        return valInt;
    }
}
